import os
import sys
import subprocess
import time
import re
import getpass
from datetime import datetime
from os.path import expanduser
import tempfile

import requests
from absl import app, flags

from coin.exchange.binance.kr_rest.native_private_client import BinanceNativePrivateClient
from coin.exchange.binance_futures_swap.kr_rest.native_private_client import BinanceSwapNativePrivateClient
from cc.appcoin2.strategy.support.util import get_cmd_vault_key_copy


class MySpotClient(BinanceNativePrivateClient):
  def __init__(self, key_file):
    super().__init__(key_file=key_file)

  def transfer(self, trsf_type, asset, amount):
    method = 'POST'
    path = '/sapi/v1/futures/transfer'
    params = {
        'type': trsf_type,
        'asset': asset,
        'amount': amount,
        'recvWindow': self._recv_window,
        'timestamp': int(time.time() * 1000),
    }
    update = self._query(method, path, params)
    return update

  def history(
      self,
      asset,
  ):
    method = 'GET'
    path = '/sapi/v1/futures/transfer'
    params = {
        'asset': asset,
        'startTime': int((time.time() - 86400 * 5) * 1000),
        'size': 100,
        'recvWindow': self._recv_window,
        'timestamp': int(time.time() * 1000),
    }
    update = self._query(method, path, params)
    return update


def init_flags():
  flags.DEFINE_string('account', 'xunke18_swap', 'account name')
  flags.DEFINE_string('usdt', '500', 'usdt amount')
  flags.DEFINE_string('server', 'strategy-04.ap-northeast-1.megaport', 'server')
  flags.DEFINE_bool('query_only', True, 'query only mode')
  flags.DEFINE_string('deno', 'USDT', 'denominated ccy, USDT or BUSD for now')
  flags.DEFINE_bool('buy', False, 'after transfer, buy the bnb in spot')
  flags.DEFINE_bool('spot_to_swap', False, 'spot to usdt swap money')
  flags.DEFINE_bool('show_history', False,
                    'show transaction history from 5 days ago with limit 100 items')


def print_balance(spot_npc, swap_npc):
  bal = spot_npc.query_account().msg
  print("\n#####################")
  print("-- SPOT --")
  bnb_qty = 0.0
  for ass in bal['balances']:
    if ass['asset'] in [flags.FLAGS.deno, 'BNB']:
      print(f"{ass['asset']}: {ass['free']}")
    if ass['asset'] == 'BNB':
      bnb_qty = float(ass['free'])
  print("-- SWAP --")
  bal = swap_npc.query_account().msg
  for ass in bal['assets']:
    if ass['asset'] in [flags.FLAGS.deno, 'BNB']:
      print(f"{ass['asset']}: {ass['walletBalance']}")
  print("######################\n")
  return bnb_qty


def print_history(histories):
  for row in histories:
    trans_ts = int(row['timestamp'] / 1000)
    utc_dt = datetime.utcfromtimestamp(trans_ts)
    row['timestamp'] = utc_dt.strftime("%Y-%m-%d %H:%m:%S")
    print(row)


def main(_):
  account = flags.FLAGS.account
  required_usdt = float(flags.FLAGS.usdt)
  username = getpass.getuser()
  with tempfile.NamedTemporaryFile(suffix='.json') as key_file:
    key_file = key_file.name
    vault_key_name = f"trade/{account}/binance/fut_trade_key.json"
    cmd = get_cmd_vault_key_copy(vault_key_name, key_file)
    output = subprocess.check_output(cmd, shell=True)
    msc = MySpotClient(key_file=key_file)
    bsnpc = BinanceSwapNativePrivateClient(key_file=key_file, api_host='fapi.binance.com')

    curr_bnb_qty = print_balance(msc, bsnpc)

    addr = f"https://api.binance.com/api/v3/aggTrades?symbol=BNB{flags.FLAGS.deno}&limit=1"
    print(addr)
    last_trade = requests.get(addr).json()
    last_price = -1
    if len(last_trade) == 1:
      last_price = float(last_trade[0]['p'])
    print(f"current BNB price: {last_price}\n")

    if flags.FLAGS.show_history:
      print(
          "### type -> 1(from spot to USDT-Ⓜ), 2(from USDT-Ⓜ to spot), 3(from spot to COIN-Ⓜ), and 4(from COIN-Ⓜ to spot)"
      )
      deno = flags.FLAGS.deno
      print(f"$$ {deno}")
      his_deno_msg = msc.history(deno).msg
      if 'rows' in his_deno_msg:
        print_history(his_deno_msg['rows'])
      print(f"$$ BNB")
      his_bnb_msg = msc.history("BNB").msg
      if 'rows' in his_bnb_msg:
        print_history(his_bnb_msg['rows'])
    elif not flags.FLAGS.query_only and flags.FLAGS.spot_to_swap:
      print_balance(msc, bsnpc)
      print("*********TRANSFER START*********")
      trsf_msg = msc.transfer(1, flags.FLAGS.deno, required_usdt).msg
      if 'tranId' not in trsf_msg:
        print(f"ERROR sending {flags.FLAGS.deno} from spot to swap failed")
        return
      print(f"{flags.FLAGS.deno} (Spot -> Swap): {trsf_msg}")
      print("*********TRANSFER END*********")
      print_balance(msc, bsnpc)
    elif not flags.FLAGS.query_only and last_price > 0:
      # 1: transfer from spot account to USDT-Ⓜ futures account.
      # 2: transfer from USDT-Ⓜ futures account to spot account.
      # 3: transfer from spot account to COIN-Ⓜ futures account.
      # 4: transfer from COIN-Ⓜ futures account to spot account.
      required_bnb_qty = required_usdt / last_price
      print(f"required {flags.FLAGS.deno} amount: {required_usdt}")
      print(f"current BNB price: {last_price}")
      print(f"required BNB qty: {required_bnb_qty}")
      if not flags.FLAGS.buy and curr_bnb_qty < required_bnb_qty:
        print("\nNot Enough BNB, QUIT!")
      else:
        print("*********TRANSFER START*********")
        trsf_msg = msc.transfer(2, flags.FLAGS.deno, required_usdt).msg
        if 'tranId' not in trsf_msg:
          print(f"ERROR sending {flags.FLAGS.deno} from swap to spot failed")
          return

        print(f"{flags.FLAGS.deno} (Swap -> Spot): {trsf_msg}")
        if flags.FLAGS.buy:
          with tempfile.NamedTemporaryFile() as logfile:
            required_bnb_qty *= 0.98  # 2% buffer ensure we can buy the bnb
            cmd = "GLOG_logtostderr=1 "\
              "../coin_binary/coin2-bazel-bin-3.10/cc/appcoin2/support/approach_target_position "\
              f"--mea=Spot.Binance.v1.snapshot --symbol=BNB-{flags.FLAGS.deno} "\
              f"--key_path={key_file} "\
              f"--pass_bp=0 --lot_size={required_bnb_qty} --target_position={required_bnb_qty} --stickiness=2 "\
              "--use_target_position_as_delta"
            process = subprocess.Popen(cmd, shell=True, stderr=logfile, stdout=logfile)
            with open(logfile.name, 'r') as reader:
              while process.poll() is None:
                line = reader.read()
                sys.stdout.write(line)
                time.sleep(1)
                if 'Invalid API-key, IP, or permissions for action.' in line:
                  print("ERROR: API has no *spot* permissions, reverting")
                  reader.seek(0, 2)  # move to the end of the file
                  process.kill()
                  break
              sys.stdout.write(reader.read())
            with open(logfile.name, 'r') as reader:
              info = re.findall(r"notional ([\d\.]+),", reader.read())
              print("fill info:", info)
            total_paid = 0
            for paid in info:
              total_paid += round(float(paid), 4)
            new_bnb_qty = print_balance(msc, bsnpc)
            print(f"send BNB {new_bnb_qty - curr_bnb_qty}",
                  msc.transfer(1, "BNB", new_bnb_qty - curr_bnb_qty).msg)
            print(f"send {flags.FLAGS.deno} {required_usdt - total_paid}")
            trsf_msg = msc.transfer(1, flags.FLAGS.deno, required_usdt - total_paid).msg
        else:
          trsf_msg = msc.transfer(1, "BNB", required_bnb_qty).msg
        print(f"BNB (Spot -> Swap): {trsf_msg}")
        print("*********TRANSFER END*********")
        print_balance(msc, bsnpc)


if __name__ == '__main__':
  init_flags()
  app.run(main)

# Usage
# ./pyrunner python/experimental/taekwon/binance_bnb_transfer.py --account=xunke02_swap --usdt=50 --query_only=False
# ./pyrunner python/experimental/taekwon/binance_bnb_transfer.py --account=xunke11_swap --usdt=20 --server=strategy-26.ap-northeast-1.megaport --deno=BUSD --buy --query_only=False
# ./pyrunner python/experimental/taekwon/binance_bnb_transfer.py --account=xunke28_swap --usdt=35000 --query_only=False --spot_to_swap
# ./pyrunner python/experimental/taekwon/binance_bnb_transfer.py --account=xunke28_swap --show_history
# I1118 05:18:54.960705 299188 approach_target_position_strategy.cc:68] cumulative fill qty 0.362, notional 98.5726, vwap 272.3
